/* Barometer with digital and graphic display of atmospheric pressure data. Components: Screen text LCD 2004, platform: arduinio nano/uno (Atmega328 or more), pressure sensor: BME 280. Amatroskin 2022. */ #include #include #include "GyverButton.h" #include #include #define SEALEVELPRESSURE_HPA (933,17) // Set the height #define BTN_PIN 3 //Button Pin #define BASE_PERIOD 675000 //Main array acquisition period 675000ms = 11.25 min (*16 bars = 3 hours) maximum graph resolution #define MIN_VAL 990 //The minimum value displayed on the chart #define MAX_VAL 1035 //The maximum value displayed on the graph LiquidCrystal_I2C lcd(0x27, 20, 4); //We create the necessary objects GButton butt1(BTN_PIN); Adafruit_BME280 bme; uint32_t tmr1, tmr2; //timer variables uint32_t set_period = BASE_PERIOD; //Display period, changes in multiples of 3 hours (3, 6, 12, 24) int16_t plot_array[20]; //Data array for drawing a chart uint16_t base_array [128]; //The base array stores all measurements for the last 24 hours (128 cells * 11.25 minutes = 1440 minutes = 24 hours) int16_t value, delta; //Current (taken) readings, difference in readings for a selected period of time byte interval = 1; //Interval displayed on the screen (time difference between adjacent chart bars) // 11.25 min * 16 = 3 hours, 22.5 min - 6 hours, 45 min - 12 hours, 90 min - 24 hours void setup() { read_all (); // Serial.begin(9600); attachInterrupt(1, isr, CHANGE); butt1.setDebounce(80); // anti-bounce setting (default 80 ms) butt1.setTimeout(300); // hold timeout setting (default 500ms) lcd.init(); lcd.backlight(); lcd.clear(); if (!bme.begin(0x76)) { //Initialization of the BME280 sensor at address 0x76 (default) //Serial.println("Could not find a valid BME280!");//Printing an Error Message to the Port Monitor lcd.setCursor(3, 1); lcd.print(F("No connection")); //___________________________or on the screen lcd.setCursor(5, 2); lcd.print(F("to sensor")); while (1); //won't go without a sensor. } if (!digitalRead(BTN_PIN)) { //Reset settings when turned on with the button held down for (byte i = 0; i < 128; i++) base_array [i] = 0; //Erase data update_all (); //Save lcd.setCursor(5, 1); //Report it lcd.print(F("Reset data")); lcd.setCursor(9, 2); lcd.print(F("OK")); } while (!digitalRead(BTN_PIN)); lcd.clear(); initPlot(); //Initializing Symbols for Rendering value = round ((bme.seaLevelForAltitude(700, bme.readPressure())/100));//We take the current readings, convert to mm Hg. base_array[0] = value; get_data (); } void isr() { //We poll the button in the interrupt to catch the click anyway butt1.tick(); } void loop() { butt1.tick(); //Poll button if (butt1.isClick()) { //When you press: interval *= 2; //Switching the chart scale if (interval > 8) interval = 1; set_period = BASE_PERIOD * interval; //Recalculate the interval for updating data from the sensor get_data (); //Update information on the display } if (millis() - tmr1 >= BASE_PERIOD) { //We collect the basic array of data tmr1 = millis(); //Every 11.25 minutes we take readings from the sensor for (int i = 126; i >= 0; i--) { //Shift the entire array by one point base_array[i + 1] = base_array[i]; } value = round ((bme.seaLevelForAltitude(700, bme.readPressure())/100));//Convert to mm/Hg. base_array[0] = value; //Write the latest readings to an array update_all (); //And remember in EEPROM } if (millis() - tmr2 >= set_period) { //After a period of time set_period (milliseconds) tmr2 = millis(); //Refreshing the data on the screen get_data (); } } void get_data () { //We draw 16 columns of the diagram on the screen for (int i = 15; i >= 0; i--) { drawPlot(0, 3, 16, 4, MIN_VAL, MAX_VAL, (base_array[i * interval])); } delta = ((base_array[0]) - (base_array[15 * interval])); //We calculate the delta (pressure change) for the selected interval screen_data (value, delta, (interval * 3)); //Displaying text information on the screen } void screen_data (int value, int delta, byte interval) { //The function of displaying text information on the screen (everything except for the chart bars) lcd.setCursor(16, 0); lcd.print(value); lcd.setCursor(17, 2); if (delta == value) delta = 0; if (delta > 0) { lcd.print("+"); } else if (delta < 0) { lcd.print("-"); } else if (delta == 0) { lcd.print(" "); } lcd.setCursor(18, 2); lcd.print(abs(delta)); if (abs(delta) < 10) { lcd.setCursor(19, 2); lcd.print(" "); } lcd.setCursor(17, 1); lcd.print("hPa"); lcd.setCursor(17, 3); lcd.print(interval); (interval < 10) ? lcd.print("h ") : lcd.print("h"); } void initPlot() { // necessary symbols for work // created in http://maxpromer.github.io/LCD-Character-Creator/ byte row8[8] = {0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row7[8] = {0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row6[8] = {0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row5[8] = {0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111}; byte row3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111}; byte row2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111}; byte row1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}; lcd.createChar(0, row8); lcd.createChar(1, row1); lcd.createChar(2, row2); lcd.createChar(3, row3); lcd.createChar(4, row4); lcd.createChar(5, row5); lcd.createChar(6, row6); lcd.createChar(7, row7); } //Diagram drawing, code taken from Guyver https://alexgyver.ru/lcd-plots-and-bars/ void drawPlot(byte pos, byte row, byte width, byte height, int min_val, int max_val, int fill_val) { for (byte i = 0; i < width; i++) { plot_array[i] = plot_array[i + 1]; } fill_val = constrain(fill_val, min_val, max_val); plot_array[width - 1] = fill_val; for (byte i = 0; i < width; i++) { // each parameter column int infill, fract; // find the number of whole blocks, taking into account the minimum and maximum, to display on the chart infill = floor((float)(plot_array[i] - min_val) / (max_val - min_val) * height * 10); fract = (infill % 10) * 8 / 10; // find the number of remaining stripes infill = infill / 10; for (byte n = 0; n < height; n++) { // for all graph lines if (n < infill && infill > 0) { // while we're below the level lcd.setCursor(i, (row - n)); // fill in cells lcd.write(0); } if (n >= infill) { // if you reach the level lcd.setCursor(i, (row - n)); if (fract > 0) lcd.write(fract); // заполняем дробные ячейки else lcd.write(16); // if fractional == 0, fill empty for (byte k = n + 1; k < height; k++) { // everything that is on top is filled with empty lcd.setCursor(i, (row - k)); lcd.write(16); } break; } } } } void update_all () { //Обновляем данные в EEPROM eeprom_update_block((void*)&base_array, 0, sizeof(base_array)); } void read_all () { //Reading data from EEPROM eeprom_read_block((void*)&base_array, 0, sizeof(base_array)); }